package cn.iocoder.yudao.module.system.service.iam.impl;

import cn.iocoder.yudao.module.system.service.iam.IamService;
import cn.iocoder.yudao.module.system.service.iam.dto.IAMAccessTokenReqDto;
import cn.iocoder.yudao.module.system.service.iam.dto.IAMClientTokenReqDto;
import cn.iocoder.yudao.module.system.service.iam.enums.CodeMsg;
import cn.iocoder.yudao.module.system.service.iam.response.IAMAccessTokenInfo;
import cn.iocoder.yudao.module.system.service.iam.response.IAMTokenResDto;
import com.alibaba.druid.util.StringUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

@Service
@Slf4j
public class IamServiceImpl implements IamService {

    private static String GRANT_TYPE = "grant_type=";
    private static String CLIENT_ID = "client_id=";
    private static String CLIENT_SECRET = "client_secret=";
    private static String REDIRECT_URI = "redirect_uri=";
    private static String AUTHORIZE_CODE = "code=";
    private static String MEDIA_TYPE = "application/x-www-form-urlencoded";
    private static String ACCESS_TOKEN_EQUAL = "access_token=";
    private static String ACCESS_TOKEN = "access_token";

    @Value("${oauth.getAccessToken.url}")
    private String IAM_GET_ACCESS_TOKEN_URL = "";

    @Value("${oauth.getCurrentUserInfo.url}")
    private String IAM_GET_CURRENT_USER_INFO_URL = "";

    @Value("${oauth.bpm.getClientToken.url}")
    private String IAM_GET_CLIENT_TOKEN_URL = "";


    /**
     * 获取accessToken
     *
     * @param iamAccessTokenReqDto
     */
    @Override
    public String getAccessToken(IAMAccessTokenReqDto iamAccessTokenReqDto) {
        String url = IAM_GET_ACCESS_TOKEN_URL + "?" +
                CLIENT_ID + iamAccessTokenReqDto.getClientId() + "&" +
                CLIENT_SECRET + iamAccessTokenReqDto.getClientSecret() + "&" +
                GRANT_TYPE + iamAccessTokenReqDto.getGrantType() + "&" +
                REDIRECT_URI + iamAccessTokenReqDto.getRedirectUri() + "&" +
                AUTHORIZE_CODE + iamAccessTokenReqDto.getAuthIamCode();
        HttpRequestBase httpRequest = new HttpGet(url);
        String httpType = "http";
        if (url.startsWith("https")) {
            httpType = "https";
        }
        HttpClient httpClient = this.getHttpClient(httpType);
        HttpResponse httpResponse = null;
        try {
            httpResponse = httpClient.execute(httpRequest);
        } catch (IOException e) {
            log.error("请求accessToken网络失败！");
            return null;
        }
        if (httpResponse.getStatusLine().getStatusCode() != CodeMsg.SUCCESS.getCode()) {
            log.error("请求accessToken失败！statusCode:{}", httpResponse.getStatusLine().getStatusCode());
            return null;
        }
        HttpEntity respEntity = httpResponse.getEntity();
        if (respEntity == null) {
            log.error("获取accessToken失败！repEntity=null");
            return null;
        }
        //防止返回数据含中文，直接转成utf-8
        String resp = null;
        try {
            resp = EntityUtils.toString(respEntity, "utf-8");
        } catch (IOException e) {
            log.error("解析IAM实体异常！repEntity:{}", respEntity);
            return null;
        }
        if (resp == null) {
            log.error("IAM返参为空！");
            return null;
        }
        //解析返参
        IAMAccessTokenInfo iamAccessTokenInfo = JSONObject.parseObject(resp, IAMAccessTokenInfo.class);
        if (iamAccessTokenInfo == null) {
            log.error("IAM返参为空！");
            return null;
        }
        return iamAccessTokenInfo.getAccess_token();
    }

    /**
     * 获取账户信息
     *
     * @param accessToken
     */
    @Override
    public String getUsernameByAccessToken(String accessToken) {
        String url = IAM_GET_CURRENT_USER_INFO_URL + "?" +
                ACCESS_TOKEN_EQUAL + accessToken;
        HttpRequestBase httpRequest = new HttpGet(url);
        String httpType = "http";
        if (url.startsWith("https")) {
            httpType = "https";
        }
        HttpClient httpClient = this.getHttpClient(httpType);
        HttpResponse httpResponse = null;
        try {
            httpResponse = httpClient.execute(httpRequest);
        } catch (IOException e) {
            log.error("请求当前登录用户信息失败！");
            return null;
        }
        if (httpResponse == null || httpResponse.getStatusLine() == null ||
                httpResponse.getStatusLine().getStatusCode() != CodeMsg.SUCCESS.getCode()) {
            return null;
        }
        HttpEntity respEntity = httpResponse.getEntity();
        String resp = null;
        try {
            resp = EntityUtils.toString(respEntity, "utf-8");
        } catch (IOException e) {
            log.error("转换当前登录用户信息失败！");
            return null;
        }
        log.info("接收数据: " + resp);
        JSONObject jsonResp = (JSONObject) JSON.parse(resp);
        //todo 这里用对象
        return jsonResp.getString("employeeNum");
    }


    /**
     * 获取clientToken
     *
     * @param iamClientTokenReqDto
     */
    @Override
    public String getClientToken(IAMClientTokenReqDto iamClientTokenReqDto) {
        OkHttpClient client = new OkHttpClient().newBuilder().build();
        String contentParam =
                GRANT_TYPE + iamClientTokenReqDto.getGrantType() + "&" +
                        CLIENT_ID + iamClientTokenReqDto.getClientId() + "&" +
                        CLIENT_SECRET + iamClientTokenReqDto.getClientSecret();
        RequestBody body = RequestBody.create(MediaType.parse(MEDIA_TYPE), contentParam);
        Request request = new Request.Builder()
                .url(IAM_GET_CLIENT_TOKEN_URL)
                .method("POST", body)
                .addHeader("Content-Type", MEDIA_TYPE)
                .build();
        try {
            Response response = client.newCall(request).execute();
            if (response == null || response.body() == null) {
                log.error("response.body()为空");
                return null;
            }
            String bodyData = response.body().string();
            IAMTokenResDto iamTokenResDto = JSONObject.parseObject(bodyData, IAMTokenResDto.class);
            if (iamTokenResDto == null || iamTokenResDto.getData() == null) {
                log.error("iamTokenResDto或data为空, iamTokenResDto:{}", iamTokenResDto);
                return null;
            }
            return iamTokenResDto.getData().getToken();
        } catch (Exception e) {
            log.error("向IAM系统请求token，异常，e:{}", e);
            return null;
        }
    }


    private HttpClient getHttpClient(String httpType) {
        org.apache.http.client.HttpClient httpClient = null;
        if ("https".equals(httpType)) {
            try {
                httpClient = new SSLHttpClient();
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            httpClient = new DefaultHttpClient();
        }
        return httpClient;
    }

    class SSLHttpClient extends DefaultHttpClient {
        public SSLHttpClient() throws Exception {
            SSLContext ctx = SSLContext.getInstance("TLS");
            X509TrustManager tm = new X509TrustManager() {
                @Override
                public void checkClientTrusted(X509Certificate[] chain,
                                               String authType) throws CertificateException {
                }

                @Override
                public void checkServerTrusted(X509Certificate[] chain,
                                               String authType) throws CertificateException {
                }

                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }
            };
            ctx.init(null, new TrustManager[]{tm}, null);
            SSLSocketFactory ssf = new SSLSocketFactory(ctx,
                    SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
            ClientConnectionManager ccm = this.getConnectionManager();
            SchemeRegistry sr = ccm.getSchemeRegistry();
            sr.register(new Scheme("https", 443, ssf));
        }
    }


}
